// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <algorithm>

#include "base/macros.h"
#include "net/cert/internal/extended_key_usage.h"
#include "net/der/input.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

    // Helper method to check if an EKU is present in a std::vector of EKUs.
    bool HasEKU(const std::vector<der::Input>& list, const der::Input& eku)
    {
        for (const auto& oid : list) {
            if (oid == eku)
                return true;
        }
        return false;
    }

    // Check that we can read multiple EKUs from an extension.
    TEST(ExtendedKeyUsageTest, ParseEKUExtension)
    {
        // clang-format off
  const uint8_t raw_extension_value[] = {
      0x30, 0x14,  // SEQUENCE (20 bytes)
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02  // 1.3.6.1.5.5.7.3.2
      // end of SEQUENCE
  };
        // clang-format on
        der::Input extension_value(raw_extension_value);

        std::vector<der::Input> ekus;
        EXPECT_TRUE(ParseEKUExtension(extension_value, &ekus));

        EXPECT_EQ(2u, ekus.size());
        EXPECT_TRUE(HasEKU(ekus, ServerAuth()));
        EXPECT_TRUE(HasEKU(ekus, ClientAuth()));
    }

    // Check that an extension with the same OID present multiple times doesn't
    // cause an error.
    TEST(ExtendedKeyUsageTest, RepeatedOid)
    {
        // clang-format off
  const uint8_t extension_bytes[] = {
      0x30, 0x14,  // SEQUENCE (20 bytes)
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01  // 1.3.6.1.5.5.7.3.1
  };
        // clang-format on
        der::Input extension(extension_bytes);

        std::vector<der::Input> ekus;
        EXPECT_TRUE(ParseEKUExtension(extension, &ekus));
        EXPECT_EQ(2u, ekus.size());
        for (const auto& eku : ekus) {
            EXPECT_EQ(ServerAuth(), eku);
        }
    }

    // Check that parsing an EKU extension which contains a private OID doesn't
    // cause an error.
    TEST(ExtendedKeyUsageTest, ParseEKUExtensionGracefullyHandlesPrivateOids)
    {
        // clang-format off
  const uint8_t extension_bytes[] = {
    0x30, 0x13,  // SEQUENCE (19 bytes)
    0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
    0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
    0x06, 0x07,  // OBJECT IDENTIFIER (7 bytes)
    0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79  // 1.3.6.1.4.1.11129
  };
        // clang-format on
        der::Input extension(extension_bytes);

        std::vector<der::Input> ekus;
        EXPECT_TRUE(ParseEKUExtension(extension, &ekus));
        EXPECT_EQ(2u, ekus.size());
        EXPECT_TRUE(HasEKU(ekus, ServerAuth()));

        const uint8_t google_oid[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79 };
        der::Input google(google_oid);
        EXPECT_TRUE(HasEKU(ekus, google));
    }

    // Test a variety of bad inputs.

    // If the extension value has data following the sequence of oids, parsing it
    // should fail.
    TEST(ExtendedKeyUsageTest, ExtraData)
    {
        // clang-format off
  const uint8_t extra_data[] = {
      0x30, 0x14,  // SEQUENCE (20 bytes)
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02,  // 1.3.6.1.5.5.7.3.2
      // end of SEQUENCE
      0x02, 0x01,  // INTEGER (1 byte)
      0x01  // 1
  };
        // clang-format on

        std::vector<der::Input> ekus;
        EXPECT_FALSE(ParseEKUExtension(der::Input(extra_data), &ekus));
    }

    // Check that ParseEKUExtension only accepts a sequence containing only oids.
    // This test case has an integer in the sequence (which should fail). A key
    // difference between this test case and ExtendedKeyUsageTest.ExtraData is where
    // the sequence ends - in this test case the integer is still part of the
    // sequence, while in ExtendedKeyUsageTest.ExtraData the integer is after the
    // sequence.
    TEST(ExtendedKeyUsageTest, NotAnOid)
    {
        // clang-format off
  const uint8_t not_an_oid[] = {
      0x30, 0x0d,  // SEQUENCE (13 bytes)
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
      0x02, 0x01,  // INTEGER (1 byte)
      0x01  // 1
      // end of SEQUENCE
  };
        // clang-format on

        std::vector<der::Input> ekus;
        EXPECT_FALSE(ParseEKUExtension(der::Input(not_an_oid), &ekus));
    }

    // Checks that the list of oids passed to ParseEKUExtension are in a sequence,
    // instead of one or more oid tag-length-values concatenated together.
    TEST(ExtendedKeyUsageTest, NotASequence)
    {
        // clang-format off
  const uint8_t not_a_sequence[] = {
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01  // 1.3.6.1.5.5.7.3.1
  };
        // clang-format on

        std::vector<der::Input> ekus;
        EXPECT_FALSE(ParseEKUExtension(der::Input(not_a_sequence), &ekus));
    }

    // A sequence passed into ParseEKUExtension must have at least one oid in it.
    TEST(ExtendedKeyUsageTest, EmptySequence)
    {
        const uint8_t empty_sequence[] = { 0x30, 0x00 }; // SEQUENCE (0 bytes)

        std::vector<der::Input> ekus;
        EXPECT_FALSE(ParseEKUExtension(der::Input(empty_sequence), &ekus));
    }

    // The extension value must not be empty.
    TEST(ExtendedKeyUsageTest, EmptyExtension)
    {
        std::vector<der::Input> ekus;
        EXPECT_FALSE(ParseEKUExtension(der::Input(), &ekus));
    }

} // namespace

} // namespace net
